﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using System.Web.Combined;
using System.Web.Management;
using System.Web.Mvc;
using www.dotnetwise.com.desktop.v1_0;
//using CodeSmith.Data;
//using CodeSmith.Data.Caching;
//using CodeSmith.Data.Linq;

namespace System.Web.Combined
{

    /// <summary>The DesktopConfig class</summary>
    /// <created author="laurentiu.macovei" date="Tue, 09 Mar 2010 16:40:33 GMT"/>
    public static class LibConfig
    {

        #region Fields


        #region Const

        public static readonly bool LocalizationLoadComments;
        public static readonly MinifierDebugMode DebugJavascriptMode;
        public static readonly MinifierDebugMode DebugCssMode;
        public static readonly bool DebugJs;
        public static readonly bool DebugCss;
        public static readonly string RootPath;
        public static readonly string RootImages;
        public static readonly string RootCss;
        public static readonly string RootLibraries;
        public static readonly string RootRuntimeScripts;
        public static readonly string RootImagesPath;
        public static readonly string RootCssPath;
        public static readonly string RootLibrariesPath;
        public static readonly string RootRuntimeScriptsPath;
        public static readonly library[] Libraries = new library[0];
        public static readonly Dictionary<string, int> JavascriptFiles = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

        public static readonly bool EnableStaticDomains;
        public static readonly string[] StaticDomainsHttp = new string[0];
        public static readonly string[] StaticDomains = new string[0];
        public static readonly string[] SupportedLanguages = new string[0];
        #endregion Const


        #endregion Fields


        #region Constructors

        /// <summary>Static constructor of DesktopConfig</summary>
        /// <created author="laurentiu.macovei" date="Tue, 09 Mar 2010 16:40:33 GMT"/>
        static LibConfig()
        {
            var app = ConfigurationManager.AppSettings;
            SupportedLanguages = (app["SupportedLanguages"] ?? string.Empty).Split(StringSplitOptions.RemoveEmptyEntries, '\n', ',', ';')
                .Select(d => d.Trim())
                .Where(d => !string.IsNullOrEmpty(d))
                .ToArray();
            SupportedLanguages = SupportedLanguages.Contains("*") ? SupportedLanguages.Take(0).ToArray() : SupportedLanguages;

            StaticDomains = (app["StaticDomains"] ?? string.Empty).Split(StringSplitOptions.RemoveEmptyEntries, '\n', ',', ';')
                .Select(d => d.Trim())
                .Where(d => !string.IsNullOrEmpty(d))
                .ToArray();
            StaticDomainsHttp = StaticDomains.Select(d => "http://" + d).ToArray();
            EnableStaticDomains = app["EnableStaticDomains"].IsTrue() && StaticDomains.Length > 0;

            LocalizationLoadComments = app["LocalizationLoadComments"].IsTrue();
            
            if (app["RootRuntimeScripts"] != null)
            {
                var server = System.Web.HttpContext.Current.Server;
                DebugJavascriptMode = (MinifierDebugMode)Enum.Parse(typeof(MinifierDebugMode), app["DebugJavascript"] ?? "ClearTrue", true);
                DebugCssMode = (MinifierDebugMode)Enum.Parse(typeof(MinifierDebugMode), app["DebugCss"] ?? "ClearTrue", true);
                DebugCss = DebugCssMode.BitwiseAnd(MinifierDebugMode.True);
                DebugJs = DebugJavascriptMode.BitwiseAnd(MinifierDebugMode.True);

                RootImages = app["RootImages"] ?? "/css/i/";
                RootCss = app["RootCss"] ?? "/css/runtime/";
                RootLibraries = app["RootLibraries"] ?? "/lib/";
                RootRuntimeScripts = app["RootRuntimeScripts"] ?? "/lib/runtime/";
                var Server = HttpContext.Current.Server;
                RootImagesPath = Server.MapPath(RootImages);
                RootCssPath = Server.MapPath(RootCss);
                RootLibrariesPath = Server.MapPath(RootLibraries);
                RootRuntimeScriptsPath = Server.MapPath(RootRuntimeScripts);
                RootPath = Server.MapPath("~");

                if (RootPath != null)
                {
                    var files = Directory.GetFiles(RootLibrariesPath, "*.js.xml", SearchOption.AllDirectories);
                    var javascriptFiles = Directory.GetFiles(RootLibrariesPath, "*.js", SearchOption.AllDirectories);
                    Libraries = new library[files.Length];
                    int index = 0, javascriptLibraryIndex = 1;
                    foreach (var filePath in files)
                    {
                        library lib;
                        try
                        {
                            lib = library.Load(filePath);
                            lib.FilePath = filePath;
                            Libraries[index++] = lib;
                        }
                        catch (Exception ex)
                        {
                            throw new ConfigurationErrorsException("Error loading file '" + filePath + "'! Make sure it respects the targeted schema!", ex);
                        }
                        if (lib.files != null)
                            foreach (var lib_files in lib.files)
                                if (lib_files.file != null)
                                {
                                    foreach (var item in lib_files.file)
                                    {
                                        var path = Path.GetDirectoryName(filePath);
                                        var fileName = item.name ?? "";
                                        var files_path = lib_files.path;
                                        if (!string.IsNullOrEmpty(files_path))
                                        {
                                            if (files_path.StartsWith("/"))
                                                files_path = Path.Combine(RootPath, files_path.Substring(1));
                                            else files_path = Path.Combine(path, files_path);
                                        }

                                        if (fileName.StartsWith("/"))
                                            fileName = Path.Combine(RootPath, fileName.Substring(1));
                                        else path = Path.Combine(path, fileName);
                                        path = path.Replace('/', '\\');
                                        if (!JavascriptFiles.ContainsKey(path))
                                            JavascriptFiles[path] = javascriptLibraryIndex;
                                    }
                                    javascriptLibraryIndex++;
                                }
                    }
                    foreach (var filePath in javascriptFiles)
                        if (!JavascriptFiles.ContainsKey(filePath))
                            JavascriptFiles[filePath] = javascriptLibraryIndex++;
                }

            }
        }


        #endregion Constructors


        #region Properties

        ///// <summary>Gets the Libraries</summary>
        ///// <created author="laurentiu.macovei" date="Mon, 19 Apr 2010 17:02:38 GMT"/>
        //public static List<library> Libraries
        //{
        //    get
        //    {
        //        if (_Libraries == null)
        //        {
        //            var db = new DbDataContext();
        //            _Libraries = LoadLibraries(db).ToList();
        //        }
        //        return _Libraries;
        //    }
        //}

        ///// <summary>Gets the Modules</summary>
        ///// <created author="laurentiu.macovei" date="Tue, 20 Apr 2010 14:11:53 GMT"/>
        //public static List<library> Modules
        //{
        //    get
        //    {
        //        if (_Modules == null)
        //        {
        //            var db = new DbDataContext();
        //            _Modules = LoadModules(db).ToList();
        //        }
        //        return _Modules;
        //    }
        //}

        ///// <summary>Gets the Modules</summary>
        ///// <created author="laurentiu.macovei" date="Tue, 20 Apr 2010 14:11:53 GMT"/>
        //public static List<ThemeEntry> Themes
        //{
        //    get
        //    {
        //        if (_Themes == null)
        //        {
        //            var db = new DbDataContext();
        //            _Themes = LoadThemes(db).ToList();
        //        }
        //        return _Themes;
        //    }
        //}


        #endregion Properties


        #region Util Methods
        /// <summary>Returns the bitwise and operation between the value and bitwiseWith</summary>
        /// <param name="value"></param>
        /// <param name="bitwiseWith"></param>
        /// <returns></returns>
        /// <created author="laurentiu.macovei" date="Sat, 26 Jun 2010 21:19:10 GMT"/>
        public static bool BitwiseAnd(this MinifierDebugMode value, MinifierDebugMode bitwiseWith)
        {
            return (value & bitwiseWith) == bitwiseWith;
        }

        ///// <summary>Loads modules in a future maneer</summary>
        ///// <param name="db"></param>
        ///// <returns></returns>
        ///// <created author="laurentiu.macovei" date="Tue, 20 Apr 2010 14:42:01 GMT"/>
        //private static IEnumerable<library> LoadModules(DbDataContext db)
        //{
        //    return db.DesktopModule
        //            .Where(module => module.Data != null && module.Active)
        //            .FutureCache(CacheManager.GetProfile("Static")
        //            .AddCacheDependency(db.Connection.Database, db.DesktopModule)
        //            .WithCacheItemRemovedCallback(delegate { _Modules = null; }))
        //            .Select(lib => ((library)lib.Data).SetModuleId(lib.ModuleId));
        //    //.Select(module => Utils.DeserializeXmlObject<ModuleEntry>(module.Data.CreateReader()).Prepare(module.Name, module.ModuleId));//, db.DesktopModule, db.DesktopLog)
        //}

        ///// <summary>Loads libraries in a future maneer</summary>
        ///// <param name="db"></param>
        ///// <returns></returns>
        ///// <created author="laurentiu.macovei" date="Tue, 20 Apr 2010 14:44:01 GMT"/>
        //private static IEnumerable<library> LoadLibraries(DbDataContext db)
        //{
        //    return db.DesktopLibrary
        //            .Where(library => library.Data != null && library.Active)
        //            .FutureCache(CacheManager.GetProfile("Static")
        //                .AddCacheDependency(db.Connection.Database, db.DesktopLibrary)
        //                .WithCacheItemRemovedCallback(delegate { _Libraries = null; }))
        //            .Select(lib => (library)lib.Data);
        //}

        ///// <summary>Loads themes in a future maneer</summary>
        ///// <param name="db"></param>
        ///// <returns></returns>
        ///// <created author="laurentiu.macovei" date="Tue, 29 Jun 2010 22:22:44 GMT"/>
        //private static IEnumerable<ThemeEntry> LoadThemes(DbDataContext db)
        //{
        //    return db.DesktopTheme
        //            .Where(theme => theme.Data != null && theme.Active)
        //            .FutureCache(CacheManager.GetProfile("Static")
        //                .AddCacheDependency(db.Connection.Database, db.DesktopTheme)
        //                .WithCacheItemRemovedCallback(delegate { _Themes = null; }))//(string key, object value, CacheItemRemovedReason reason)
        //            .Select(library => Utils.DeserializeXmlObject<ThemeEntry>(library.Data.CreateReader()).Prepare(library.Name));

        //}


        #endregion Util Methods


        #region Business Methods

        /// <summary>
        /// </summary>
        /// <param name="word"></param>
        /// <param name="power"></param>
        /// <returns></returns>
        /// <created author="laurentiu.macovei" date="Tue, 29 Jun 2010 22:22:44 GMT"/>
        public static bool ShouldIncludeLibrary(byte[] word, int power)
        {
            var byteIndex = power / 8;
            if (word == null || word.Length <= byteIndex)
                return true;
            return (word[byteIndex] & (byte)(1 << (power % 8))) == 0;
        }

        /// <summary>
        /// </summary>
        /// <param name="word"></param>
        /// <param name="power"></param>
        /// <created author="laurentiu.macovei" date="Tue, 29 Jun 2010 22:22:44 GMT"/>
        internal static void AppendLibrary(ref byte[] word, int power)
        {
            var byteIndex = power / 8;
            if (word == null)
                word = new byte[byteIndex + 1];
            else if (word.Length <= byteIndex)
            {
                var currentWord = word;
                word = new byte[byteIndex + 1];
                for (int i = currentWord.Length - 1; i >= 0; i--)
                    word[i] = currentWord[i];
            }
            word[byteIndex] |= (byte)(1 << (power % 8));
        }

        #endregion Business Methods


        #region Fields


        #region Const

        private static readonly Regex CssUrlRegex =
			new Regex(@"url\(([\'\""]?)(?<url>.*?)\1\)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

        private static readonly Regex ImportUrlRegex =
	new Regex(@"\@import ?(url\()?([\'\""]?)(?<url>.*)\2\)?(?<media>.*)?\;", RegexOptions.IgnoreCase | RegexOptions.Compiled);

        #endregion Const


        #region Static

        //private static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        #endregion Static


        #endregion Fields


        #region Util Methods

        /// <summary>Initializes the runtime scripts, libraries and css</summary>
        /// <created author="laurentiu.macovei" date="Sat, 26 Jun 2010 21:20:04 GMT"/>
        public static void InitializeRuntimeLibrariesScriptsAndCss()
        {
            var rootCssPath = LibConfig.RootCssPath;
            var imgRootUrl = LibConfig.RootImages;
            var rootImagesPath = LibConfig.RootImagesPath;
            var scriptsRuntimePath = LibConfig.RootRuntimeScriptsPath;
            var webPath = HttpContext.Current.Server.MapPath("~");
            EnsureFolder(scriptsRuntimePath, true);
            EnsureFolder(rootCssPath, true);
            EnsureFolder(rootImagesPath, true);


            //read the assembly name from file
            //to refresh the cache
            var versionFileName = "5CB1D4C3DC9940DB9BA918617A609A30.version";
            var versionPath = Path.Combine(scriptsRuntimePath, versionFileName);
            var cleanScriptsAndRebuildCssAndImages = false;
            var assemblyVersion = Assembly.GetCallingAssembly().GetName().Version.ToString() + "/"
                    + Assembly.GetExecutingAssembly().GetName().Version.ToString();
            if (File.Exists(versionPath))
            {
                var fileVersion = string.Empty;
                using (var sw = new StreamReader(versionPath))
                    fileVersion = sw.ReadToEnd();
                if (fileVersion != assemblyVersion)
                    cleanScriptsAndRebuildCssAndImages = true;
            }
            else cleanScriptsAndRebuildCssAndImages = true;
            if (cleanScriptsAndRebuildCssAndImages)// || LibConfig.DebugJavascriptMode.BitwiseAnd(MinifierDebugMode.NotPersist))
                ClearFolder(scriptsRuntimePath, null,
                    dir => ".svn".Equals(Path.GetFileName(dir)));
            if (cleanScriptsAndRebuildCssAndImages)// || LibConfig.DebugJavascriptMode.BitwiseAnd(MinifierDebugMode.NotPersist))
                using (var sw = new StreamWriter(versionPath))
                    sw.Write(assemblyVersion);

            if (cleanScriptsAndRebuildCssAndImages)// || LibConfig.DebugCssMode.BitwiseAnd(MinifierDebugMode.NotPersist))
                ClearFolder(rootCssPath,
                    file => "web.config".Equals(Path.GetFileName(file), StringComparison.OrdinalIgnoreCase),
                    dir => ".svn".Equals(Path.GetFileName(dir)));
            if (cleanScriptsAndRebuildCssAndImages && LibConfig.DebugCssMode.BitwiseAnd(MinifierDebugMode.False) && !Directory.EnumerateDirectories(rootCssPath).Any())
            {
                ClearFolder(rootImagesPath, null, null);
                var rootLibrariesPath = LibConfig.RootLibrariesPath;
                var rootPath = LibConfig.RootPath;
                var validNameChars = "abcdefghijklmnopqrstuvwxyz0123456789";
                var validNameCharsLength = validNameChars.Length;
                var imageIndex = 0;
                var getNextIndex = new Func<string>(() =>
                {
                    string result = "";
                    var i = imageIndex++;
                    do
                    {
                        result = validNameChars[i % validNameCharsLength] + result;
                        i /= validNameCharsLength;
                    }
                    while (i-- > 0);
                    return result;
                });
                var allImages = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                foreach (var filePath in Directory.GetFiles(rootLibrariesPath, "*.js.xml", SearchOption.AllDirectories))
                {
                    var lib = library.Load(filePath);
                    if (lib.css != null)
                        foreach (var css in lib.css)
                        {
                            var path = Path.GetDirectoryName(filePath);
                            if (!string.IsNullOrWhiteSpace(css.path))
                                path = Path.Combine(path, css.path);
                            path = Path.GetFullPath(path);
                            if (css.file != null)
                                foreach (var file in css.file)
                                {
                                    var cssFilePath = Path.GetFullPath(Path.Combine(path, file.name));
                                    if (!cssFilePath.StartsWith(webPath))
                                        throw new AccessViolationException(".js.xml libraries cannot access paths outside of the website folder! Path requested: " + cssFilePath);
                                    string content;
                                    try
                                    {
                                        using (var sr = new StreamReader(cssFilePath))
                                            content = sr.ReadToEnd();
                                    }
                                    catch
                                    {
                                        //move to the next file silently
                                        continue;
                                    }
                                    var root = rootLibrariesPath;
                                    while (!path.StartsWith(root, StringComparison.OrdinalIgnoreCase) && path.Length > 3)
                                        throw new AccessViolationException(".js.xml libraries cannot access paths outside of the website folder! Path requested: " + path);
                                    if (!root.EndsWith("/") && !root.EndsWith("\\"))
                                        root += Path.DirectorySeparatorChar;
                                    var newPath = Path.Combine(rootCssPath, path.Substring(root.Length));
                                    newPath = Path.Combine(newPath, file.name);
                                    EnsureFolder(Path.GetDirectoryName(newPath), false);
                                    using (var sw = new StreamWriter(newPath, false))
                                        sw.Write(ReplaceImagesUrl(imgRootUrl, rootImagesPath, rootPath, getNextIndex, allImages, Path.GetDirectoryName(cssFilePath), content));
                                }
                        }
                }
            }
        }

        /// <summary>
        /// Replaces images url with a relative url to the minified css url. 
        /// <para>The image is copied to the ImagesRoot folder</para>
        /// </summary>
        /// <param name="imgRootUrl"></param>
        /// <param name="rootImagesPath"></param>
        /// <param name="rootPath"></param>
        /// <param name="getNextIndex"></param>
        /// <param name="allImages"></param>
        /// <param name="path"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        /// <created author="laurentiu.macovei" date="Fri, 30 Dec 2011 20:35:04 GMT"/>
        private static string ReplaceImagesUrl(string imgRootUrl, string rootImagesPath, string rootPath, Func<string> getNextIndex, Dictionary<string, string> allImages, string path, string content)
        {
            //process images
            var result = CssUrlRegex.Replace(content,
                match =>
                {
                    var url = match.Groups["url"].Value;
                    var uri = new Uri(url, UriKind.RelativeOrAbsolute);
                    string imageName;
                    if (uri.IsAbsoluteUri || string.IsNullOrWhiteSpace(url) || ".css".Equals(Path.GetExtension(url), StringComparison.OrdinalIgnoreCase))
                        imageName = match.Captures[0].Value;
                    else
                    {
                        var imgPath = url.StartsWith("/")
                            ? Path.Combine(rootPath, url.Substring(1))
                            : Path.Combine(path, url);
                        imgPath = imgPath.Replace('/', '\\');
                        if (!allImages.TryGetValue(imgPath, out imageName))
                        {
                            if (File.Exists(imgPath))
                            {
                                imageName = Path.ChangeExtension(getNextIndex(), Path.GetExtension(url));
                                var imgNewPath = Path.Combine(rootImagesPath, imageName);
                                File.Copy(imgPath, imgNewPath);
                                imageName = "url(" + UriExtensions.StaticContent(imgRootUrl + imageName) + ")";
                            }
                            else
                            {
                                imageName = match.Captures[0].Value;//leave the url untouched
                            }
                            allImages[imgPath] = imageName;
                        }
                    }
                    return imageName;
                });
            //process @import url("path"); tags
            result = ImportUrlRegex.Replace(result,
                match =>
                {
                    var url = match.Groups["url"].Value;
                    var uri = new Uri(url, UriKind.RelativeOrAbsolute);
                    string importName;
                    if (uri.IsAbsoluteUri)
                        importName = match.Captures[0].Value;
                    else
                    {
                        var importCssFilePath = url.StartsWith("/")
                            ? Path.Combine(rootPath, url.Substring(1))
                            : Path.Combine(path, url);
                        importCssFilePath = Path.GetFullPath(importCssFilePath);
                        string fileContent;
                        using (var sr = new StreamReader(importCssFilePath))
                            fileContent = "/* Imported content from " + importCssFilePath + " */\n" + sr.ReadToEnd();
                        var minifiedContent = ReplaceImagesUrl(imgRootUrl, rootImagesPath, rootPath, getNextIndex, allImages, Path.GetDirectoryName(importCssFilePath), fileContent);
                        var media = match.Groups["media"].Value;
                        if (!string.IsNullOrWhiteSpace(media))
                            minifiedContent = "@media " + media + "{\n" + minifiedContent + "\n}";
                        return minifiedContent;
                    }
                    return importName;
                });
            return result;
        }

        /// <summary>Ensures that the given path exists and marks it hidden or not</summary>
        /// <param name="path"></param>
        /// <param name="hidden"></param>
        /// <created author="laurentiu.macovei" date="Sat, 26 Jun 2010 21:20:04 GMT"/>
        private static void EnsureFolder(string path, bool hidden)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
                if (hidden)
                    File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
            }
        }

        /// <summary>Clears the given path except files or folders</summary>
        /// <param name="path"></param>
        /// <param name="exceptFile"></param>
        /// <param name="exceptDir"></param>
        /// <created author="laurentiu.macovei" date="Sat, 26 Jun 2010 16:18:10 GMT"/>
        private static void ClearFolder(string path, Func<string, bool> exceptFile, Func<string, bool> exceptDir)
        {
            if (Directory.Exists(path))
            {
                foreach (var file in Directory.GetFiles(path, "*.*"))
                    if (exceptFile == null || !exceptFile(file))
                    {
                        try { File.Delete(file); }
                        catch { }
                    }
                foreach (var dir in Directory.GetDirectories(path))
                    if (exceptDir == null || !exceptDir(dir))
                    {
                        try { Directory.Delete(dir, true); }
                        catch { }
                    }
            }
        }


        #endregion Util Methods

    }
    /// <summary>
    /// Specifies values of debugging js/css mode
    /// </summary>
    [Flags]
    public enum MinifierDebugMode
    {
        False = 1,
        True = 2,
        NotPersist = 4,
        Clear = 6,
        ClearFalse = 5,
        ClearTrue = 6
    }

}
namespace System.Web.Mvc
{
    /// <summary>The UriExtensions class</summary>
    /// <created author="laurentiu.macovei" date="Fri, 30 Dec 2011 20:35:04 GMT"/>
    public static partial class UriExtensions
    {
        /// <summary>Returns the current culture name picked from RouteData or CultureInfo.CurrentCulture</summary>
        /// <param name="viewContext">The view context to extend to</param>
        /// <created author="laurentiu.macovei" date="Wed, 25 Jan 2012 11:52:49 GMT"/>
        public static string CultureName(this ViewContext viewContext)
        {
            var culture = System.Globalization.CultureInfo.CurrentCulture;
            var cultureName = viewContext.RouteData.Values["language"] as string;// culture.IsNeutralCulture ? culture.Parent.Name : culture.Name;
            if (cultureName != culture.Name && cultureName != culture.Parent.Name)
            {
                cultureName = culture.Name;
            }
            return cultureName;
        }
        /// <summary>Returns a static url content (also known as cookiless url) if EnableStaticDomains appSetting is true and there are defined static domains, otherwise returns the given content path url</summary>
        /// <param name="url">The url helper to be extended</param>
        /// <param name="contentPath">The path of the static resource</param>
        /// <created author="laurentiu.macovei" date="Fri, 30 Dec 2011 20:35:04 GMT"/>
        public static string StaticContent(this UrlHelper url, string contentPath)
        {
            if (!LibConfig.EnableStaticDomains)
                return url.Content(contentPath ?? string.Empty);
            contentPath = contentPath ?? string.Empty;
            //if (contentPath.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
            //    || contentPath.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
            //    return contentPath;
            var uri = url.RequestContext.HttpContext.Request.Url;
            if (!uri.IsDefaultPort || uri.Scheme != "http")
            {
                uri = new Uri(uri.Scheme + "://" + LibConfig.StaticDomains[Math.Abs(contentPath.GetHashCode()) % LibConfig.StaticDomains.Length] + ":" + uri.Port, UriKind.Absolute);
                uri = new Uri(uri, contentPath);
                return url.Content(uri.ToString());
            }
            return LibConfig.StaticDomainsHttp[Math.Abs(contentPath.GetHashCode()) % LibConfig.StaticDomains.Length] + contentPath;
        }
        /// <summary>Returns a static url content (also known as cookiless url) if EnableStaticDomains appSetting is true and there are defined static domains, otherwise returns the given content path url</summary>
        /// <param name="contentPath">The path of the static resource</param>
        /// <created author="laurentiu.macovei" date="Fri, 30 Dec 2011 20:35:04 GMT"/>
        public static string StaticContent(string contentPath)
        {
            if (!LibConfig.EnableStaticDomains)
                return contentPath ?? string.Empty;
            contentPath = contentPath ?? string.Empty;
            //var uri = HttpContext.Current.Request.Url;
            return new Uri(new Uri(/*uri.Scheme*/
            "http://" + LibConfig.StaticDomains[Math.Abs(contentPath.GetHashCode()) % LibConfig.StaticDomains.Length] + ":"/* + uri.Port*/, UriKind.Absolute), contentPath).ToString();
        }

        /// <summary>
        /// Concatenates the given scripts to viewContext.Controller.ViewBag.Scripts so the master layout will render them.
        /// <para>This way you can register scripts from partial views as well</para>
        /// </summary>
        /// <param name="viewContext">The viewContext to extend</param>
        /// <param name="scripts">The list of scripts to be concatenated.
        /// <para>Should not start nor end with with ';'</para>
        /// </param>
        /// <returns></returns>
        /// <created author="laurentiu.macovei" date="Tue, 03 Jan 2012 19:16:33 GMT"/>
        public static List<string> RegisterScripts(this ViewContext viewContext, params string[] scripts)
        {
            var viewData = viewContext.Controller.ViewData;
            var scriptsList = viewData["Scripts"] as List<string>;
            if (scriptsList == null)
                viewData["Scripts"] = scriptsList = new List<string>();
            if (scripts != null && scripts.Length > 0)
                scriptsList.AddRange(scripts.Except(scriptsList, StringComparer.OrdinalIgnoreCase));
            return scriptsList;
        }
        /// <summary>Returns all the registered scripts</summary>
        /// <param name="viewContext">The view context to return string.Join(";", viewContext.Controller.ViewBag.Scripts)</param>
        /// <returns></returns>
        /// <created author="laurentiu.macovei" date="Tue, 03 Jan 2012 19:21:29 GMT"/>
        public static string GetRegisteredScripts(this ViewContext viewContext)
        {
            var scripts = viewContext.Controller.ViewData["Scripts"] as List<string>;
            return scripts == null ? null : string.Join(";", scripts);
        }


        /// <summary>
        /// Concatenates the given scripts to viewContext.Controller.ViewBag.Scripts so the master layout will render them.
        /// <para>This way you can register scripts from partial views as well</para>
        /// </summary>
        /// <param name="viewContext">The viewContext to extend</param>
        /// <param name="topScriptsUrl">The list of scripts to be concatenated.</param>
        /// <created author="laurentiu.macovei" date="Sat, 31 Mar 2012 21:55:08 GMT"/>
        public static List<string> RegisterTopScripts(this ViewContext viewContext, params string[] topScriptsUrl)
        {
            var viewbag = viewContext.Controller.ViewData;
            var topScripts = viewbag["TopScripts"] as List<string>;
            if (topScripts == null)
                viewbag["TopScripts"] = topScripts = new List<string>();
            if (topScriptsUrl != null)
                foreach (var topScriptUrl in topScriptsUrl)
                {
                    if (!string.IsNullOrEmpty(topScriptUrl) && !topScripts.Any(c => string.Equals(topScriptUrl, c, StringComparison.OrdinalIgnoreCase)))
                        topScripts.Add(topScriptUrl);
                }
            return topScripts;
        }
        /// <summary>Returns all the registered top scripts as individual links</summary>
        /// <param name="viewContext">The view context to return the registered css links</param>
        /// <created author="laurentiu.macovei" date="Sat, 31 Mar 2012 21:54:56 GMT"/>
        public static string GetRegisteredTopScripts(this ViewContext viewContext)
        {
            var topScripts = viewContext.Controller.ViewData["TopScripts"] as List<string>;
            var urlHelper = new UrlHelper(viewContext.RequestContext);
            return topScripts == null ? null : string.Join(null, topScripts.Select(c => "\n<script type='text/javascript' src='" + HttpUtility.HtmlAttributeEncode(urlHelper.StaticContent(c)) + "'></script>"));
        }

        /// <summary>
        /// Concatenates the given css to viewContext.Controller.ViewBag.CssUrls so the master layout will render them.
        /// <para>This way you can register css from partial views as well</para>
        /// </summary>
        /// <param name="viewContext">The viewContext to extend</param>
        /// <param name="cssUrl">The list of scripts to be concatenated.</param>
        /// <param name="location">Specify the location where this script should be rendered. Defaults to HeadTop</param>
        /// <created author="laurentiu.macovei" date="Mon, 02 Apr 2012 17:53:43 GMT"/>
        public static List<KeyValuePair<string, ScriptLocation>> RegisterCustomScript(this ViewContext viewContext, string cssUrl, ScriptLocation location = ScriptLocation.HeadTop)
        {
            var viewbag = viewContext.Controller.ViewBag;
            var cssList = viewbag.CssUrls as List<KeyValuePair<string, ScriptLocation>>;
            if (cssList == null)
                viewbag.CssUrls = cssList = new List<KeyValuePair<string, ScriptLocation>>();
            if (!string.IsNullOrEmpty(cssUrl) && !cssList.Any(c => string.Equals(cssUrl, c.Key, StringComparison.OrdinalIgnoreCase) && location == c.Value))
                cssList.Add(new KeyValuePair<string, ScriptLocation>(cssUrl, location));
            return cssList;
        }
        /// <summary>Returns all the registered css</summary>
        /// <param name="viewContext">The view context to return the registered css links</param>
        /// <param name="location"></param>
        /// <created author="laurentiu.macovei" date="Mon, 02 Apr 2012 17:53:44 GMT"/>
        public static string GetRegisteredCustomScripts(this ViewContext viewContext, ScriptLocation location)
        {
            var scripts = viewContext.Controller.ViewBag.CssUrls as List<KeyValuePair<string, ScriptLocation>>;
            var urlHelper = new UrlHelper(viewContext.RequestContext);
            return scripts == null ? null : string.Join(null, scripts.Where(s => s.Value == location).Select(c => "\n<script type='text/javascript' src='" + HttpUtility.HtmlAttributeEncode(urlHelper.StaticContent(c.Key)) + "'></script>"));
        }


        /// <summary>
        /// Concatenates the given css to viewContext.Controller.ViewBag.CssUrls so the master layout will render them.
        /// <para>This way you can register css from partial views as well</para>
        /// </summary>
        /// <param name="viewContext">The viewContext to extend</param>
        /// <param name="cssUrl">The list of scripts to be concatenated.</param>
        /// <param name="media">Specify the media where this css applies. Defaults to all</param>
        /// <created author="laurentiu.macovei" date="Sun, 22 Jan 2012 13:42:12 GMT"/>
        public static List<KeyValuePair<string, string>> RegisterCss(this ViewContext viewContext, string cssUrl, string media = "all")
        {
            var viewbag = viewContext.Controller.ViewBag;
            var cssList = viewbag.CssUrls as List<KeyValuePair<string, string>>;
            if (cssList == null)
                viewbag.CssUrls = cssList = new List<KeyValuePair<string, string>>();
            if (!string.IsNullOrEmpty(cssUrl) && !cssList.Any(c => string.Equals(cssUrl, c.Key, StringComparison.OrdinalIgnoreCase) && string.Equals(media, c.Value, StringComparison.OrdinalIgnoreCase)))
                cssList.Add(new KeyValuePair<string, string>(cssUrl, media));
            return cssList;
        }
        /// <summary>Returns all the registered css</summary>
        /// <param name="viewContext">The view context to return the registered css links</param>
        /// <created author="laurentiu.macovei" date="Sun, 22 Jan 2012 13:42:12 GMT"/>
        public static string GetRegisteredCss(this ViewContext viewContext)
        {
            var cssUrls = viewContext.Controller.ViewBag.CssUrls as List<KeyValuePair<string, string>>;
            var urlHelper = new UrlHelper(viewContext.RequestContext);
            return cssUrls == null ? null : string.Join(null, cssUrls.Select(c => "\n<link rel='stylesheet' href='" + HttpUtility.HtmlAttributeEncode(urlHelper.StaticContent(c.Key)) + "' media='" + HttpUtility.HtmlAttributeEncode(c.Value) + "' type='text/css' />"));
        }
    }
    public enum ScriptLocation { HeadTop, HeadBottom, }
}